import {
  cadAppErrorLog,
  CategoryType,
  DocumentObject,
  frameUtils,
  IdObj,
  IShapeData,
  pickedFaceData,
  pickedLineData,
  pickedModelData,
  pickedVertexData,
  PickType,
  ReferenceCsys,
  ReferenceLine,
  ReferencePlane,
  Types,
} from '@amcax/base';
import { Renderer3D, RendererMgr } from '@amcax/renderer';
import {
  ctx2,
  dataManager,
  docMgr,
  previewer,
  referenceManager,
} from '@app-cad/module';
import { Object3D, Vector3, Vector3Tuple } from 'three-legacy';
import { pickedProductData } from '../components/pick-input/types';

export const previewData = async (
  modelsData:
    | IShapeData[]
    | {
        [key: number]: IShapeData;
        type: Types;
      },
  profile?: any,
) => {
  if (Array.isArray(modelsData)) {
    ctx2.viewer.sceneSetup.adjustCameraForVisualData(
      modelsData as IShapeData[],
    );
  }

  const renderer = RendererMgr.getRender<Renderer3D>('3d');

  const referenceParent = new Object3D();
  let models: Object3D[] = [];
  let hasProfileRender = false;

  const previewPlaneByProfile = (profile: {
    location: Vector3Tuple;
    axis: Vector3Tuple;
  }) => {
    const plane = new ReferencePlane({
      center: profile?.location,
      normal: profile?.axis,
      camera: ctx2.viewer.sceneSetup.oCamera,
    });
    plane.addTo(referenceParent, 0xff8000);
  };

  // 坐标轴
  if (profile?.frame) {
    const newCsys = new ReferenceCsys(
      profile.frame,
      ctx2.viewer.sceneSetup.oCamera,
    );

    newCsys.addTo(referenceParent);
    hasProfileRender = true;
    // 无限平面
  } else if (profile?.axis && profile?.location && profile?.infinite) {
    previewPlaneByProfile(profile);
    hasProfileRender = true;
    // 无限直线
  } else if (profile?.firstPoint && profile?.lastPoint && profile?.infinite) {
    const line = new ReferenceLine({
      start: profile?.firstPoint,
      end: profile?.lastPoint,
      camera: ctx2.viewer.sceneSetup.oCamera,
    });
    line.addTo(referenceParent, 0xff8000);
    hasProfileRender = true;
  } else if (!Array.isArray(modelsData)) {
    // copy plane results
    if (modelsData?.type === Types.PLANE_COPY && Array.isArray(profile)) {
      profile.forEach(previewPlaneByProfile);

      hasProfileRender = true;
    }
  } else if (modelsData[0]?.category != null) {
    models = renderer.parsePreviewModels(modelsData);
  }

  if (models?.length > 0 || hasProfileRender) {
    previewer.preview(hasProfileRender ? [...models, referenceParent] : models);
    ctx2.viewer.requestRender();
  } else {
    throw new Error('预览模型渲染失败');
  }
};

export const getIdOfPickedObj = (
  obj?:
    | pickedFaceData
    | pickedLineData
    | pickedVertexData
    | pickedModelData
    | pickedProductData,
) => {
  return (
    (obj as pickedFaceData)?.meshId ??
    (obj as pickedLineData)?.edgeId ??
    (obj as pickedVertexData)?.vertexId ??
    (obj as pickedModelData)?.idInfo?.o ??
    (obj as pickedProductData)?.productId
  );
};

export const getNameOfPickedObj = (
  picked:
    | pickedFaceData
    | pickedLineData
    | pickedVertexData
    | pickedModelData
    | pickedProductData,
) => {
  if (picked == null) {
    return '';
  }

  if ((picked as pickedProductData).assemblyInfo != null) {
    return (picked as pickedProductData).assemblyInfo.title;
  }

  const projectInfo = docMgr.flatDocumentObjects;

  if ((picked as pickedVertexData).assisObj?.name === 'cspoint') {
    const pickedVertex = picked as pickedVertexData;
    const isPointOnModel = pickedVertex.vertexId.includes(':v');
    const mainDxid = isPointOnModel
      ? pickedVertex.vertexId.split(':')[0]
      : pickedVertex.vertexId;

    const label =
      projectInfo.find((p) => p.dxid === mainDxid)?.label ||
      pickedVertex.objInfo?.label;

    return isPointOnModel ? `${label}上的点 ${pickedVertex.idInfo.i}` : label;
  }

  if ((picked as pickedModelData).assisObj?.name === 'pickedObject') {
    const pickedModel = picked as pickedModelData;

    return pickedModel?.objInfo?.label;
  }

  if ([PickType.CURVE, PickType.LINE].includes(picked.idInfo.t)) {
    const pickedLine = picked as pickedLineData;

    const label = pickedLine.objInfo?.label;

    const modelData = pickedLine.modelData;

    if (/^.*:(X|Y|Z)(-AXIS)?$/.test(pickedLine.edgeId)) {
      return `${label} 的轴 ${pickedLine.edgeId.replace('-AXIS', '').slice(-1)}`;
    }

    const isPickSelf =
      (modelData?.faces == null || modelData?.faces?.length === 0) &&
      modelData?.edges?.length === 1;

    return isPickSelf ? label : `${label}上的曲线 ${pickedLine.idInfo.i}`;
  }

  if ([PickType.SURFACE, PickType.PLANE].includes(picked.idInfo.t)) {
    const pickedFace = picked as pickedFaceData;
    const modelData = pickedFace.modelData;

    const isPickSelf =
      (picked as pickedFaceData).objInfo.type !== Types.Sketcher &&
      modelData?.faces?.length === 1;

    const label = pickedFace.objInfo?.label;

    if (/^.*:(XOY|YOZ|XOZ)$/.test(pickedFace.meshId)) {
      return `${label} 的面 ${pickedFace.meshId.slice(-3)}`;
    }

    return isPickSelf ? label : `${label}的面 ${pickedFace.idInfo.i}`;
  }

  return (
    picked as
      | pickedFaceData
      | pickedLineData
      | pickedVertexData
      | pickedModelData
  )?.objInfo?.label;
};

export const getDocumentObjectRenderType = (
  documentObject?: DocumentObject,
  partKey?: string,
) => {
  if (!documentObject) {
    cadAppErrorLog(
      'documentObject is null while getting document object render type',
    );

    return null;
  }
  if (frameUtils.isFrameAxesChildDxid(documentObject.dxid)) {
    return PickType.LINE;
  }
  if (frameUtils.isFramePlaneChildDxid(documentObject.dxid)) {
    return PickType.PLANE;
  }

  const shape = dataManager.getShapeData(documentObject.shape, partKey);
  const referenceTarget = referenceManager.getObjectById(documentObject.dxid);

  const isReferenceEdge = referenceTarget instanceof ReferenceLine;
  const isReferencePlane = referenceTarget instanceof ReferencePlane;

  if (
    (shape?.category as CategoryType) === 'POINT' ||
    documentObject.dxid === 'ORIGIN'
  ) {
    return PickType.VERTEX;
  }

  const isShapeEdge = !(shape?.faces?.length > 0) && shape?.edges?.length === 1;

  if (isShapeEdge || isReferenceEdge) {
    const isCurve = shape?.edges[0].mesh.length > 2;
    return isCurve ? PickType.CURVE : PickType.LINE;
  }

  const isShapeFace = shape?.faces?.length === 1;

  if (isShapeFace || isReferencePlane) {
    const isSurface = shape?.faces[0].normals.length > 1;
    return isSurface ? PickType.SURFACE : PickType.PLANE;
  }

  return null;
};

// note: the function is not used for assemblies because
//       measure objects cannot be picked from the assembly tree,
//       and therefore, no partKey is passed to getDocumentObjectRenderType
export const getRenderTypeByDxid = (dxid: string) => {
  return getDocumentObjectRenderType(docMgr.getDocumentObjectById(dxid));
};

/**
 * 获取面法向量
 * @param idInfo
 * @param partKey, optional to identify the part of an assembly
 */
export const getFaceNormal = (idInfo: IdObj, partKey?: string) => {
  const objInfo = dataManager.getObjectInfoByDxid(idInfo.o);

  const shapeData = ctx2.dataManager.getShapeData(idInfo.o, partKey);

  if (shapeData != null) {
    const normals = shapeData?.faces?.[idInfo.i]?.normals;
    if (normals.length === 1) {
      return new Vector3(...normals[0]).normalize();
    }

    return null;
  }

  if (objInfo.type === Types.PLANE) {
    return new Vector3(...objInfo.params.axis);
  }

  if (objInfo.type === Types.FRAME) {
    const zAxis = new Vector3(...objInfo.params.frame[1]);
    const xAxis = new Vector3(...objInfo.params.frame[2]);

    const yAxis = zAxis.clone().cross(xAxis);

    return [xAxis, yAxis, zAxis][idInfo.i];
  }

  if (objInfo.type === Types.BASIC_PLANE) {
    return new Vector3(
      ...{
        XOY: [0, 0, 1],
        XOZ: [0, 1, 0],
        YOZ: [1, 0, 0],
      }[idInfo.o],
    );
  }

  return null;
};
